# Copyright 2019 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Configure local Resource Compilers for every available target platform.

Platform support:

- Linux/macOS/non-Windows: this repository rule is almost a no-op.
  It generates an empty BUILD file, and does not actually discover any
  toolchains. The register_local_rc_exe_toolchains() method is a no-op.

- Windows: this repository rule discovers the Windows SDK path, the installed
  rc.exe compilers. The register_local_rc_exe_toolchains() registers a toolchain
  for each rc.exe compiler.

Usage:

    load("//src/main/res:winsdk_configure.bzl", "winsdk_configure")

    winsdk_configure(name = "local_config_winsdk")

    load("@local_config_winsdk//:toolchains.bzl", "register_local_rc_exe_toolchains")

    register_local_rc_exe_toolchains()
"""

load("@bazel_tools//tools/cpp:cc_configure.bzl", "MSVC_ENVVARS")
load("@bazel_tools//tools/cpp:windows_cc_configure.bzl", "find_vc_path", "setup_vc_env_vars")

# Keys: target architecture, as in <Windows-SDK-path>/<target-architecture>/bin/rc.exe
# Values: corresponding Bazel CPU value under @platforms//cpu:*
_TARGET_ARCH = {
    "arm": "arm",
    "arm64": "aarch64",
    "x64": "x86_64",
    "x86": "x86_32",
}

def _find_rc_exes(root):
    result = {}
    for a in _TARGET_ARCH:
        exe = root.get_child(a).get_child("rc.exe")
        if exe.exists:
            result[a] = str(exe)
    return result

def _find_all_rc_exe(repository_ctx):
    if not repository_ctx.os.name.startswith("windows"):
        return {}

    vc = find_vc_path(repository_ctx)
    if vc:
        env = setup_vc_env_vars(
            repository_ctx,
            vc,
            envvars = [
                "WindowsSdkDir",
                "WindowsSdkVerBinPath",
            ],
            allow_empty = True,
            escape = False,
        )

        # Try the versioned directory.
        sdk = env.get("WindowsSdkVerBinPath")
        if sdk:
            archs = _find_rc_exes(repository_ctx.path(sdk))
            if archs:
                return archs

        # Try the unversioned directory (typically Windows 8.1 SDK).
        sdk = env.get("WindowsSdkDir")
        if sdk:
            archs = _find_rc_exes(repository_ctx.path(sdk).get_child("bin"))
            if archs:
                return archs
    return {}

def _toolchain_defs(repository_ctx, rc_exes):
    if not rc_exes:
        return ""

    result = ["""# Auto-generated by winsdk_configure.bzl
load(
    "@io_bazel//src/main/res:winsdk_toolchain.bzl",
    "WINDOWS_RESOURCE_COMPILER_TOOLCHAIN_TYPE",
    "windows_resource_compiler_toolchain",
)"""]

    for arch, rc_path in rc_exes.items():
        wrapper = "rc_%s.bat" % arch
        repository_ctx.file(
            wrapper,
            content = "@\"%s\" %%*" % rc_path,
            executable = True,
        )

        result.append(
            """
windows_resource_compiler_toolchain(
    name = "local_{arch}_tc",
    rc_exe = "{wrapper}",
)

toolchain(
    name = "local_{arch}",
    exec_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:{cpu}",
    ],
    toolchain = ":local_{arch}_tc",
    toolchain_type = WINDOWS_RESOURCE_COMPILER_TOOLCHAIN_TYPE,
    visibility = ["//visibility:public"],
)
""".format(
                arch = arch,
                wrapper = wrapper,
                cpu = _TARGET_ARCH[arch],
            ),
        )

    return "\n".join(result)

def _toolchain_labels(repository_ctx, rc_exes):
    tc_labels = [
        "\"@{repo}//:local_{arch}\"".format(repo = repository_ctx.name, arch = arch)
        for arch in rc_exes
    ]
    if rc_exes:
        body = "native.register_toolchains(%s)" % ", ".join(tc_labels)
    else:
        body = "pass"

    return """# Auto-generated by winsdk_configure.bzl

def register_local_rc_exe_toolchains():
    {body}
""".format(body = body)

def _impl(repository_ctx):
    rc_exes = _find_all_rc_exe(repository_ctx)
    repository_ctx.file(
        "BUILD",
        content = _toolchain_defs(repository_ctx, rc_exes),
        executable = False,
    )

    repository_ctx.file(
        "toolchains.bzl",
        content = _toolchain_labels(repository_ctx, rc_exes),
        executable = False,
    )

winsdk_configure = repository_rule(
    implementation = _impl,
    local = True,
    environ = list(MSVC_ENVVARS),
)
